package cn.element.io.nio.chat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 实现群聊系统
 */
public class Server {
    
    // 1.定义一些成员属性,选择器,服务端通道,端口
    private Selector selector;
    
    private ServerSocketChannel ssChannel;
    
    private static final int PORT = 8848;
    
    // 2.定义初始化代码
    public Server() {
        try {
            // 创建选择器对象
            selector = Selector.open();
            
            // 获取通道
            ssChannel = ServerSocketChannel.open();
            
            // 绑定客户端连接的端口
            ssChannel.bind(new InetSocketAddress(PORT));
            
            // 设置为非阻塞通信模式
            ssChannel.configureBlocking(false);
            
            // 把通道注册到选择器上去,并开始指定连接事件
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private void listen() {
        try {
            while (selector.select() > 0) {
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                
                while (it.hasNext()) {
                    // 提取这个事件
                    SelectionKey key = it.next();
                    
                    // 判断事件类型
                    if (key.isAcceptable()) {
                        // 客户端接入请求
                        SocketChannel socketChannel = ssChannel.accept();
                        
                        // 注册为非阻塞模式
                        socketChannel.configureBlocking(false);
                        
                        // 注册给选择器,监听读事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        // 处理这个客户端的消息,接收它然后实现转发逻辑
                        readClientData(key);
                    }
                    
                    it.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 接收当前客户端通道的信息,转发给其他客户端通道
     */
    private void readClientData(SelectionKey key) {
        SocketChannel socketChannel = null;

        try {
            // 直接得到当前客户端通道
            socketChannel = (SocketChannel) key.channel();

            // 创建缓冲区对象接收客户端通道的数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int len = socketChannel.read(buffer);
            
            if (len > 0) {
                buffer.flip();
                //提取读取到的信息
                String msg = new String(buffer.array(), 0, buffer.remaining());
                System.out.println("接收到了客户端消息: " + msg);
                
                // 把这个消息推送给客户端接收
                sendMessageToAll(msg, socketChannel);
            }
        } catch (IOException e) {
            try {
                System.out.println("有人离线了 " + socketChannel.getRemoteAddress());
                key.cancel();
                socketChannel.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    /**
     * 把当前客户端消息数据推送给当前全部在线客户
     */
    private void sendMessageToAll(String msg, SocketChannel socketChannel) throws IOException {
        System.out.println("服务端开始转发这个消息,当前处理的线程: " + Thread.currentThread().getName());

        for (SelectionKey key : selector.keys()) {
            Channel channel = key.channel();

            // 不要把数据发给自己
            if (channel instanceof SocketChannel && channel != socketChannel) {
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                ((SocketChannel) channel).write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        // 创建服务端对象
        Server server = new Server();
        
        // 开始监听客户端各种消息类型
        server.listen();
    }
}
